// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <melon/benchmark.h>
#include <melon/init/init.h>

#include <pollux/functions/registerer.h>
#include <pollux/functions/lib/registration_helpers.h>
#include <pollux/functions/lib/benchmarks/FunctionBenchmarkBase.h>
#include <pollux/functions/prestosql/comparisons.h>
#include <pollux/vector/vector_fuzzer.h>

namespace kumo::pollux::functions {

void registerVectorFunctions() {
  POLLUX_REGISTER_VECTOR_FUNCTION(udf_simd_comparison_eq, "eq");
  registerBinaryScalar<EqFunction, bool>({"nonsimd_eq"});
  register_function<EqFunction, bool, IntervalDayTime, IntervalDayTime>(
      {"nonsimd_eq"});
  register_function<EqFunction, bool, IntervalYearMonth, IntervalYearMonth>(
      {"nonsimd_eq"});
}

} // namespace kumo::pollux::functions

namespace {
using namespace kumo::pollux;
using namespace kumo::pollux::exec;
using namespace kumo::pollux::functions;

class ComparisonsBechmark
    : public kumo::pollux::functions::test::FunctionBenchmarkBase {
 public:
  ComparisonsBechmark() {
    registerVectorFunctions();
  }

  RowVectorPtr createRowData(const TypePtr& type) {
    VectorFuzzer::Options opts;
    opts.nullRatio = 0;
    opts.vectorSize = 10'000;
    VectorFuzzer fuzzer(opts, execCtx_.pool());

    return fuzzer.fuzzInputFlatRow(ROW({"c0", "c1"}, {type, type}));
  }

  void run(const std::string& name, const TypePtr& type) {
    melon::BenchmarkSuspender suspender;
    auto row_vector = createRowData(type);
    auto exprSet =
        compileExpression(fmt::format("{}(c0, c1)", name), row_vector->type());
    suspender.dismiss();

    uint32_t cnt = 0;
    for (auto i = 0; i < 100; i++) {
      cnt += evaluate(exprSet, row_vector)->size();
    }
    melon::doNotOptimizeAway(cnt);
  }
};

std::unique_ptr<ComparisonsBechmark> benchmark;

BENCHMARK(non_simd_bigint_eq) {
  benchmark->run("nonsimd_eq", BIGINT());
}

BENCHMARK_RELATIVE(simd_bigint_eq) {
  benchmark->run("eq", BIGINT());
}

BENCHMARK(non_simd_integer_eq) {
  benchmark->run("nonsimd_eq", INTEGER());
}

BENCHMARK_RELATIVE(simd_integer_eq) {
  benchmark->run("eq", INTEGER());
}

BENCHMARK(non_simd_smallint_eq) {
  benchmark->run("nonsimd_eq", SMALLINT());
}

BENCHMARK_RELATIVE(simd_smallint_eq) {
  benchmark->run("eq", SMALLINT());
}

BENCHMARK(non_simd_tinyint_eq) {
  benchmark->run("nonsimd_eq", TINYINT());
}

BENCHMARK_RELATIVE(simd_tinyint_eq) {
  benchmark->run("eq", TINYINT());
}

BENCHMARK(non_simd_double_eq) {
  benchmark->run("nonsimd_eq", DOUBLE());
}

BENCHMARK_RELATIVE(simd_double_eq) {
  benchmark->run("eq", DOUBLE());
}

BENCHMARK(non_simd_real_eq) {
  benchmark->run("nonsimd_eq", REAL());
}

BENCHMARK_RELATIVE(simd_real_eq) {
  benchmark->run("eq", REAL());
}

BENCHMARK(non_simd_date_eq) {
  benchmark->run("nonsimd_eq", DATE());
}

BENCHMARK_RELATIVE(simd_date_eq) {
  benchmark->run("eq", DATE());
}

BENCHMARK(non_simd_interval_day_time_eq) {
  benchmark->run("nonsimd_eq", INTERVAL_DAY_TIME());
}

BENCHMARK_RELATIVE(simd_interval_day_time_eq) {
  benchmark->run("eq", INTERVAL_DAY_TIME());
}

BENCHMARK(non_simd_interval_year_month_eq) {
  benchmark->run("nonsimd_eq", INTERVAL_YEAR_MONTH());
}

BENCHMARK_RELATIVE(simd_interval_year_month_eq) {
  benchmark->run("eq", INTERVAL_YEAR_MONTH());
}

} // namespace

int main(int argc, char** argv) {
  melon::Init init{&argc, &argv};

  kumo::pollux::memory::MemoryManager::initialize({});

  benchmark = std::make_unique<ComparisonsBechmark>();
  melon::runBenchmarks();

  benchmark.reset();
  return 0;
}
